#ifndef __CLOUD_BACKUP_INFO__
#define __CLOUD_BACKUP_INFO__

#include <unordered_map>
#include <pthread.h>
#include "util.hpp"
#include "conf.hpp"


namespace cloud
{

#define BACKUP_INFO_MANAGER_LOGGER "backup_info_manager_logger"
    struct BackupInfo
    {
        static bool CreateNewBackupInfo(const std::string& real_path, BackupInfo* info)
        {
            FileUtil fu(real_path);
            if(!fu.Exists())
            {
                return false;
            }

            //新创建的文件，一定是热点文件
            info->_is_hot = true;
            info->_file_size = fu.FileSize();
            info->_atime = fu.LastATime();
            info->_mtime = fu.LastMTime();
            Configuration* conf = Configuration::GetInstance();
            info->_backup_path = real_path;
            info->_compress_path = conf->GetCompressDir() + fu.FileName() + conf->GetCompressSuffix();
            info->_download_url = conf->GetDownloadPrefix() + fu.FileName();
            return true;
        }

        bool _is_hot;
        int64_t _file_size;
        time_t _atime;
        time_t _mtime;
        std::string _backup_path;
        std::string _compress_path;
        std::string _download_url;
    };



    class BackupInfoManager
    {
    private:
        std::string _infomation_file;
        std::unordered_map<std::string, BackupInfo> _table;
        pthread_rwlock_t _rwlock;

    private:
        static BackupInfoManager _instance;

    private:
        BackupInfoManager() 
        {
            pthread_rwlock_init(&_rwlock, NULL);
            if(!InitLoad())
                exit(-1);
        } 

        bool InitLoad()
        {
            _infomation_file = Configuration::GetInstance()->GetInformationFile();

            std::unique_ptr<log_system::LoggerBuilder> glBuilder(new log_system::GlobalLoggerBuilder());
            glBuilder->buildLoggerName(BACKUP_INFO_MANAGER_LOGGER);
            glBuilder->buildLoggerType(log_system::LoggerType::LOGGER_ASYNC);
            glBuilder->buildSink<log_system::FileSink>("./log/backup_info_manager_logger.txt");

            log_system::Logger::ptr logger = glBuilder->build();

            FileUtil fu(_infomation_file);
            if(!fu.Exists())
            {
                logger->warnLog("加载备份信息文件时%s备份信息文件不存在", _infomation_file.c_str());

                fu.CreateParentDir();
                logger->infoLog("创建%s备份信息文件所在目录", _infomation_file.c_str());
                
                // 创建文件
                fu.SetContent("");
                logger->infoLog("创建%s备份信息文件",_infomation_file.c_str());
            }

            std::string content;
            fu.GetContent(&content);
            logger->infoLog("读取%s备份信息文件",_infomation_file.c_str());

            //文件为空Json反序列化会失败，但文件为空是正常情况，直接退出。
            if(content.empty())
            {
                return true;
            }

            Json::Value root;
            if(!JsonUtil::Unserialize(content, &root))
            {
                logger->fatalLog("反序列化%s备份信息时失败,文件内容格式有误",_infomation_file.c_str());

                return false;
            }

            for(int i = 0; i < root.size(); ++i)
            {
                BackupInfo info;

                info._is_hot = root[i]["is_hot"].asBool();
                info._file_size = root[i]["file_size"].asInt64();
                info._atime = static_cast<time_t>(root[i]["atime"].asInt64());
                info._mtime = static_cast<time_t>(root[i]["mtime"].asInt64());
                info._backup_path = root[i]["backup_path"].asString();
                info._compress_path = root[i]["compress_path"].asString();
                info._download_url = root[i]["download_url"].asString();

                _table[info._download_url] = info;
            }
            logger->infoLog("%s备份信息文件读取完毕",_infomation_file.c_str());
            return true;
        }

        void Storage()
        {
            std::vector<BackupInfo> array;
            GetAll(&array);

            Json::Value root;
            for(int i = 0; i < array.size(); ++i)
            {
                Json::Value item;
                item["is_hot"] = array[i]._is_hot;
                item["file_size"] = static_cast<Json::Int64>(array[i]._file_size);
                item["atime"] = static_cast<Json::Int64>(array[i]._atime);
                item["mtime"] = static_cast<Json::Int64>(array[i]._mtime);
                item["backup_path"] = array[i]._backup_path;
                item["compress_path"] = array[i]._compress_path;
                item["download_url"] = array[i]._download_url;
                root.append(item);
            }
            std::string content;
            JsonUtil::Serialize(root, &content);


            FileUtil fu(_infomation_file);
            fu.SetContent(content);
            log_system::Logger::ptr logger = log_system::getLogger(BACKUP_INFO_MANAGER_LOGGER);
            logger->infoLog("保存备份信息到%s文件中",_infomation_file.c_str());
        }

    public:
        static BackupInfoManager* GetInstance()
        {
            return &_instance;
        }

        ~BackupInfoManager()
        {
            pthread_rwlock_destroy(&_rwlock);
        }

        
        void Insert(const BackupInfo& info)
        {
            pthread_rwlock_wrlock(&_rwlock);
            _table[info._download_url] = info;
            log_system::Logger::ptr logger = log_system::getLogger(BACKUP_INFO_MANAGER_LOGGER);
            logger->infoLog("插入资源%s备份信息",info._download_url.c_str());
            pthread_rwlock_unlock(&_rwlock);
            Storage();
        }

        void Update(const BackupInfo& info)
        {
            pthread_rwlock_wrlock(&_rwlock);
            _table[info._download_url] = info;
            log_system::Logger::ptr logger = log_system::getLogger(BACKUP_INFO_MANAGER_LOGGER);
            logger->infoLog("更新资源%s备份信息",info._download_url.c_str());
            pthread_rwlock_unlock(&_rwlock);
            Storage();
        }

        bool GetOneByUrl(const std::string& donwload_url, BackupInfo* info)
        {
            pthread_rwlock_wrlock(&_rwlock);
            auto it = _table.find(donwload_url);
            if(it == _table.end())
            {
                pthread_rwlock_unlock(&_rwlock);
                return false;
            }
            *info = it->second;
            pthread_rwlock_unlock(&_rwlock);
            return true;
        }

        bool GetAll(std::vector<BackupInfo>* array)
        {
            pthread_rwlock_wrlock(&_rwlock);
            auto it = _table.begin();
            while(it != _table.end())
            {
                array->push_back(it->second);
                ++it;
            }
            pthread_rwlock_unlock(&_rwlock);
            return true;
        }
    };
    //饿汉单例
    BackupInfoManager BackupInfoManager::_instance;
} 


#endif